#!/bin/bash
GNAPVERSION="1.7.1"

GNAPNAME="`basename $0`"
echo "GNAP Core Building tool ${GNAPNAME} version ${GNAPVERSION}"
CATALYSTCONF="/etc/catalyst/catalyst.conf"
GNAPLIBDIR="/usr/lib/gnap"
STAGE3FILE="${GNAPLIBDIR}/gnap-stage3seed.tar.bz2"
SPECDIR="${GNAPLIBDIR}/specs"
EMERGELOG="/var/log/emerge.log"

getcols() {
	echo "$2"
}

G=$'\e[32;01m'
B=$'\e[31;01m'
N=$'\e[0m'
W=$'\e[33;01m'
K=$'\e[34;01m'
C="`stty size 2> /dev/null`"
C="`getcols ${C}`"
C=$((${C} - 7))
E=$'\e['${C}'G'

gwarn() {
	echo -e " ${W}*${N} ${*}"
}

gconfirm() {
	if [ "${FORCEYES}" == "1" ]; then
		gwarn "${*} forced to yes"
	else
		read -ep " ${W}*${N} ${*} [N]: " answer
		if [ "${answer}" != "y" -a "${answer}" != "Y" ]; then
			echo Build aborted !
			exit 2 
		fi
	fi
}

gbegin() {
	echo -ne " ${G}*${N} ${*}..."
}

gtest() {
	continued=0
	if [ "$#" -gt 0 ] && [ "$1" == "continued" ]; then
		shift
		continued=1
	fi
	
	if [ "$#" -eq 0 ] || ([ -n "$1" ] && [ "$1" -eq 0 ]); then
		if [ "${continued}" -eq 0 ]; then
			echo -e "${E}  ${K}[ ${G}ok${K} ]${N}"
		fi
	else
		echo -e "${E}  ${K}[ ${B}!!${K} ]${N}"		
		if [ "$#" -ge 2 ]; then
			shift
			echo -en " ${B}*${N} ${*}"
			echo -e "${E}  ${K}[ ${B}!!${K} ]${N}"
			echo "Build failed, try man ${GNAPNAME} for more help"
			exit 1
		fi
	fi
}

usage() {
	echo "Options:"
	echo "    -t stage            Build stage to execute (or \"all\")"
	echo "    -p snapshot_stamp   Portage snapshot to use timestamp"
	echo "    -o overlay_dir      Portage overlay directory"
	echo "    -v version_stamp    GNAP timestamp"
	echo "    -f                  Force all answers to yes"
	echo "    -s seedstagefile    File to use as a Stage3 seed tarball"
	echo "    -l logfile          Use specific log file"
	echo "    -c catalyst.conf    Use specific catalyst.conf file"
	echo "    -e specdir          Full path to alternate specs directory"
	echo
	echo "Please man ${GNAPNAME} for more details."
}

if [ "$#" -eq "0" ]||[ "$1" == "-h" ]; then
	usage
	exit 0
fi

gbegin "Checking parameters"

# Read options
NOTARGET=1
SNAPSHOTSTAMP="`date +%Y%m%d`"
while getopts ":hs:p:m:o:v:t:fl:c:e:" options; do
	case ${options} in
		h ) usage
			exit 0;;
		s ) STAGE3FILE="${OPTARG}";;
		p ) SNAPSHOTSTAMP="${OPTARG}"
			P_WAS_SPECIFIED="1";;
		o ) PORTAGE_OVERLAY="${OPTARG}";;
		v ) STAMP="${OPTARG}";;
		t ) if [ "${OPTARG}" == "all" ]; then
				ALLTARGET="1"
			fi
			if [ "${OPTARG}" == "snapshot" ]; then
				SNAPSHOT="1"
			fi
			if [ "${OPTARG}" == "stage3" ]; then
				STAGE3="1"
				NEEDS_SNAPSHOT="1"
			fi
			if [ "${OPTARG}" == "livecd-stage1" ]; then
				LIVECD1="1"
				NEEDS_SNAPSHOT="1"
			fi
			if [ "${OPTARG}" == "livecd-stage2" ]; then
				LIVECD2="1"
				NEEDS_SNAPSHOT="1"
			fi
			if [ "${OPTARG}" == "tarball" ]; then
				TARBALL="1"
			fi
			if [ "${OPTARG}" == "extensions" ]; then
				MODULES="1"
				NEEDS_SNAPSHOT="1"
			fi
			NOTARGET="0";;
		f ) FORCEYES="1";;
		l ) GNAPLOGFILE="${OPTARG}";;
		c ) CATALYSTCONF="${OPTARG}";;
		e ) SPECDIR="${OPTARG}";;
		* ) gtest 1 "Specified options are incomplete or unknown !";;
	esac
done

# Root is needed
test "${EUID}" -eq 0
gtest continued $? "You need to be root to run ${GNAPNAME}"

# catalyst.conf file
test -f "${CATALYSTCONF}"
gtest continued $? "${CATALYSTCONF} file not found !"
storedir="/var/tmp/catalyst"
source "${CATALYSTCONF}"

# common.conf file
test "${SPECDIR:0:1}" == "/"
gtest continued $? \
	"Incorrect specdir: specdir must be a full path, not a relative path"
test -f "${SPECDIR}/common.conf"
gtest continued $? "Incorrect specdir: ${SPECDIR}/common.conf not found !"
source "${SPECDIR}/common.conf"
export CHOST
export CFLAGS
export CXXFLAGS

# Default value for version stamp is snapshot stamp
if [ "${STAMP}" == "" ]; then
	STAMP="${SNAPSHOTSTAMP}"
fi

# Default targets is complete core build
if [ "${ALLTARGET}" == "1" ]; then
	if [ "${P_WAS_SPECIFIED}" != "1" ]; then
		SNAPSHOT="1"
	fi
	STAGE3="1"
	LIVECD1="1"
	LIVECD2="1"
	TARBALL="1"
	MODULES="1"
	NEEDS_SNAPSHOT="1"
fi

# At least one target is needed
test "${NOTARGET}" == "0"
gtest continued $? \
	"No target specified. You should provide at least one -t option."

# Stage3 needs a seed stage
if [ "${STAGE3}" == "1" ]; then
	test -f "${STAGE3FILE}"
	gtest continued $? "The \"-s\" option needs to designate a valid seed stage"
fi

# Snapshot must exist if snapshot phase not selected and a stage is
if [ "${NEEDS_SNAPSHOT}" == "1" -a "${SNAPSHOT}" != 1 ]; then
	test -f "${storedir}/snapshots/portage-${SNAPSHOTSTAMP}.tar.bz2"
	gtest continued $? \
		"Can't find ${storedir}/snapshots/portage-${SNAPSHOTSTAMP}.tar.bz2"
fi

# Seed stage if needed must be an existing file
if [ "${STAGE3}" == "1" ]; then
	test -f "${STAGE3FILE}"
	gtest continued $? "${STAGE3FILE} is not a valid stage3 tarball"
fi

# If livecd-stage1 and no stage3, check stage3 file exists
STAGE3LOC="${storedir}/builds/${RELTYPE}/stage3-${SUBARCH}-${STAMP}.tar.bz2"
if [ "${LIVECD1}" == "1" -a "${STAGE3}" != "1" ]; then
	test -f "${STAGE3LOC}"
	gtest continued $? \
		"[stage3] results required for livecd-stage1 build"
fi

# If extensions and no stage3, warn that we'll use seedstage as stage3
if [ "${MODULES}" == "1" -a "${STAGE3}" != "1" ]; then
	if [ ! -f "${STAGE3LOC}" ]; then
		gwarn "The extensions target was selected without the stage3 target."
		gconfirm "Should I use the seed stage as stage3 result ?"
		if [ ! -d "${storedir}/builds/${RELTYPE}" ]; then
			mkdir -p "${storedir}/builds/${RELTYPE}"
		fi
		cp "${STAGE3FILE}" "${STAGE3LOC}"
	fi
fi
gtest 0

# Explain what will get built
if [ "${SNAPSHOT}" == "1" ]; then
	TARGETLIST="[snapshot] "
fi
if [ "${STAGE3}" == "1" ]; then
	TARGETLIST="${TARGETLIST}[stage3] "
fi
if [ "${LIVECD1}" == "1" ]; then
	TARGETLIST="${TARGETLIST}[livecd-stage1] "
fi
if [ "${LIVECD2}" == "1" ]; then
	TARGETLIST="${TARGETLIST}[livecd-stage2] "
fi
if [ "${TARBALL}" == "1" ]; then
	TARGETLIST="${TARGETLIST}[tarball] "
fi
if [ "${MODULES}" == "1" ]; then
	TARGETLIST="${TARGETLIST}[extensions]"
fi
gwarn "The following targets will be called:"
gwarn "${TARGETLIST}"

# Confirm use of SNAPSHOT and -p
if [ "${SNAPSHOT}" == "1" ]; then
	if [ "${SNAPSHOTSTAMP}" != "`date +%Y%m%d`" ]; then
		gwarn "Due to the -p option, snapshot will be named ${SNAPSHOTSTAMP}"
		gconfirm "Please confirm you really want to use that snapshot stamp"
	fi
fi

# Confirm tarball overwrite if TARBALL stage selected
if [ "${TARBALL}" == "1" ]; then
	if [ -e gnap-${GNAPVERSION}-${STAMP}.tar ]; then
		gconfirm "gnap-${GNAPVERSION}-${STAMP}.tar already exists, overwrite"
	fi
fi

# Logfile setup and confirmation
if [ "$GNAPLOGFILE" == "" ]; then
	GNAPLOGFILE=./${GNAPNAME}-${STAMP}.log
fi
if [ -f "${GNAPLOGFILE}" -a "${FORCEYES}" != "1" ]; then
	read -ep \
		" ${W}*${N} $GNAPLOGFILE already exists. Append/Overwrite [A]: " answer
	if [ "${answer}" == "o" -o "${answer}" == "O" ]; then
		rm ${GNAPLOGFILE}
	fi
fi
touch ${GNAPLOGFILE}

# Snapshot phase
if [ "${SNAPSHOT}" == "1" ]; then
	if [ "${PORTAGE_OVERLAY}" != "" ]; then	
		gbegin "${G}[snapshot]${N} stage (snapshot current Portage w/ overlay)"
		catalyst -c "${CATALYSTCONF}" -C \
			target=snapshot \
			version_stamp="${SNAPSHOTSTAMP}" \
			portdir_overlay="${PORTAGE_OVERLAY}" \
			>> "${GNAPLOGFILE}" 2>&1
	else
		gbegin "${G}[snapshot]${N} stage (snapshot current Portage tree)"	
		catalyst -c "${CATALYSTCONF}" -C \
			target=snapshot \
			version_stamp="${SNAPSHOTSTAMP}" \
			>> "${GNAPLOGFILE}" 2>&1
	fi
	gtest $? "[snapshot] failed, please see ${GNAPLOGFILE} for details"
fi

# Stage3 phase
if [ "${STAGE3}" == "1" ]; then
	gbegin "${G}[stage3]${N} stage (base system build)"
	
	if [ ! -d "${storedir}/builds/${RELTYPE}" ]; then
		mkdir -p "${storedir}/builds/${RELTYPE}"
	fi
	cp "${STAGE3FILE}" "${storedir}/builds/${RELTYPE}/seedstage.tar.bz2"
	
	catalyst -c "${CATALYSTCONF}" -C \
		target=stage3 \
		subarch="${SUBARCH}" \
		rel_type="${RELTYPE}" \
		snapshot="${SNAPSHOTSTAMP}" \
		version_stamp="${STAMP}" \
		profile="${PROFILE}" \
		source_subpath="${RELTYPE}/seedstage" \
		portage_confdir="${SPECDIR}/portage_confdir" \
		>> "${GNAPLOGFILE}" 2>&1
	gtest continued $? "[stage3] failed, see ${GNAPLOGFILE} for details"
	gtest `grep "completed emerge" \\
		"${storedir}/tmp/gnap/stage3-${SUBARCH}-${STAMP}${EMERGELOG}" | \\
		tail -n 1 | awk -F "[() ]" '{print $7 != $9}'` \
		"[stage3] packages compilation failed, see ${GNAPLOGFILE} for details"
	rm "${storedir}/builds/${RELTYPE}/seedstage.tar.bz2"
fi

# LIVECD-STAGE1 phase
if [ "${LIVECD1}" == "1" ]; then
	gbegin "${G}[livecd-stage1]${N} stage (GNAP-specific packages build)"
	TEMPCONF=`mktemp`;
	gtest continued $? "Failed to make tmpfile"
	cat >> "${TEMPCONF}" <<EOF
target: livecd-stage1
subarch: ${SUBARCH}
rel_type: ${RELTYPE}
snapshot: ${SNAPSHOTSTAMP}
version_stamp: ${STAMP}
profile: ${PROFILE}
source_subpath: ${RELTYPE}/stage3-${SUBARCH}-${STAMP}
portage_confdir: ${SPECDIR}/portage_confdir
EOF
	cat "${SPECDIR}/packages.conf" >> "${TEMPCONF}"
	
	catalyst -c "${CATALYSTCONF}" -f "${TEMPCONF}" >> "${GNAPLOGFILE}" 2>&1
	gtest continued $? "[livecd-stage1] failed, see ${GNAPLOGFILE} for details"
	gtest `grep "completed emerge" \\
		"${storedir}/tmp/gnap/livecd-stage1-${SUBARCH}-${STAMP}${EMERGELOG}" |\\
		tail -n 1 | awk -F "[() ]" '{print $7 != $9}'` \
		"[livecd-stage1] compilation failed, see ${GNAPLOGFILE} for details"
	rm "${TEMPCONF}"
fi

# LIVECD-STAGE2 phase
if [ "${LIVECD2}" == "1" ]; then
	gbegin "${G}[livecd-stage2]${N} stage (kernel and LiveCD builds)"
	TEMPCONF=`mktemp`;
	gtest continued $? "Failed to make tmpfile"
	cat >> "${TEMPCONF}" <<EOF
target: livecd-stage2
subarch: ${SUBARCH}
rel_type: ${RELTYPE}
snapshot: ${SNAPSHOTSTAMP}
version_stamp: ${STAMP}
profile: ${PROFILE}
source_subpath: ${RELTYPE}/livecd-stage1-${SUBARCH}-${STAMP}
boot/kernel/gentoo/sources: =${KERNEL_SOURCES}
boot/kernel/gentoo/config: ${SPECDIR}/kernel.config
boot/kernel/gentoo/extraversion: GNAP-${GNAPVERSION}
livecd/iso: gnap-${GNAPVERSION}-${STAMP}.iso
livecd/fsscript: ${SPECDIR}/fsscript
EOF
	cat "${SPECDIR}/livecd.conf" >> "${TEMPCONF}"
	
	catalyst -c "${CATALYSTCONF}" -f "${TEMPCONF}" >> ${GNAPLOGFILE} 2>&1
	gtest $? "[livecd-stage2] failed, see ${GNAPLOGFILE} for details"
	rm "${TEMPCONF}"
fi

# TARBALL phase
if [ "${TARBALL}" == "1" ]; then
	gbegin "${G}[tarball]${N} phase (Creation of core and basefs components)"
	test -e "gnap-${GNAPVERSION}-${STAMP}.iso"
	gtest continued $? "No gnap-${GNAPVERSION}-${STAMP}.iso file to convert !"
	test -d "${storedir}/tmp/gnap/livecd-stage1-${SUBARCH}-${STAMP}"
	gtest $? "Missing livecd-stage2 results"
	
	gbegin "  Creating core component"
	TEMPMNTDIR=`mktemp -d -t gnapmnt_XXXXXXXXXX`
	gtest continued $? "Failed to make tmpfile"
	TEMPISODIR=`mktemp -d -t gnapiso_XXXXXXXXXX`
	gtest continued $? "Failed to make tmpfile"
	
	mount -o loop "gnap-${GNAPVERSION}-${STAMP}.iso" ${TEMPMNTDIR} && \
		cp -r ${TEMPMNTDIR}/* ${TEMPISODIR}
	gtest continued $? "Failed to mount ISO and copy files"
	umount "${TEMPMNTDIR}" && rm -rf "${TEMPMNTDIR}"
	gtest continued $? "Failed to cleanup ${TEMPMNTDIR}"
	
	cp "${SPECDIR}/isolinux/isolinux.cfg" ${TEMPISODIR}/isolinux/
	cp "${SPECDIR}/isolinux/syslinux.cfg" ${TEMPISODIR}/
	cp "${SPECDIR}/isolinux/boot.msg" ${TEMPISODIR}/isolinux/
	DATE=`date --utc`
	echo "GNAP-${GNAPVERSION}-${STAMP} built on ${DATE}" \
		>> "${TEMPISODIR}/isolinux/boot.msg"
	
	tar cf "gnap-${GNAPVERSION}-${STAMP}.tar" -C "${TEMPISODIR}" .
	gtest continued $? "Failed to create tarball"
	rm -rf "${TEMPISODIR}" && rm "gnap-${GNAPVERSION}-${STAMP}.iso"
	gtest $? "Tarball created, but failed to remove temporary files"

	gbegin "  Creating basefs component"
	tar jcf "gnap-basefs-${GNAPVERSION}-${STAMP}.tar.bz2" \
		-C "${storedir}/tmp/gnap/livecd-stage2-${SUBARCH}-${STAMP}" .
	gtest $? "Unable to create basefs tarball"
fi

# EXTENSIONS phase
if [ "${MODULES}" == "1" ]; then
	gbegin "${G}[extensions]${N} stage start"
	GRP_PREFIX="${storedir}/builds/${RELTYPE}/grp-${SUBARCH}-${STAMP}"
	SPECMODULE="${SPECDIR}/extensions.conf"
	mod_list=`grep '^extensions:' "${SPECMODULE}" 2>/dev/null`;
	mod_list="${mod_list/extensions:/}"
	test "${mod_list}" != ""
	gtest $? "No extension to build"
	for mod_name in $mod_list ; do
		gbegin "  Building ${mod_name} extension"
		mod_useflags=`grep "^${mod_name}/useflags:" ${SPECMODULE} 2>/dev/null`;
		mod_useflags="${mod_useflags/${mod_name}\/useflags:/}"
		mod_packlist=`grep "^${mod_name}/packlist:" ${SPECMODULE} 2>/dev/null`;
		mod_packlist="${mod_packlist/${mod_name}\/packlist:/}"
		mod_cleanup=`grep "^${mod_name}/cleanup:" ${SPECMODULE} 2>/dev/null`;
		mod_cleanup="${mod_cleanup/${mod_name}\/cleanup:/}"
		TEMPCONF=`mktemp`;
		gtest continued $? "Failed to make tmpfile"
		cat >> $TEMPCONF <<EOF
target: grp
subarch: ${SUBARCH}
rel_type: ${RELTYPE}
snapshot: ${SNAPSHOTSTAMP}
version_stamp: ${STAMP}
profile: ${PROFILE}
source_subpath: ${RELTYPE}/stage3-${SUBARCH}-${STAMP}
grp: ${mod_name}
grp/use: ${mod_useflags}
grp/${mod_name}/type: pkgset
grp/${mod_name}/packages: ${mod_packlist}
EOF
		
		catalyst -c "${CATALYSTCONF}" -f "${TEMPCONF}" >> "${GNAPLOGFILE}" 2>&1
		gtest continued $? \
			"Extension build failed, see ${GNAPLOGFILE} for details"
		rm "$TEMPCONF"

		TEMPMODULEDIR=`mktemp -d -t gnapmod_XXXXXXXXXX`
		gtest continued $? "Failed to make tmpdir"
		for pkg in $( ls ${GRP_PREFIX}/${mod_name}/All/*.tbz2 ); do
			tar jxf $pkg -C "${TEMPMODULEDIR}" >> "${GNAPLOGFILE}" 2>&1
		done
		gtest continued $? "Failed to unpack extension packages"

		mod_rmlist=""
		for cleanup in ${mod_cleanup} ; do
			cleanup=${cleanup/\.\./}
			mod_rmlist="${mod_rmlist} ${TEMPMODULEDIR}/${cleanup}"
		done
		rm -rf ${mod_rmlist}
		gtest continued $? "Failed to apply extension cleanup instructions"
	
		tar jcf "gnapext_${mod_name}.tbz2" -C "${TEMPMODULEDIR}" . \
			>> "${GNAPLOGFILE}" 2>&1
		gtest $? "Failed to build extension file"
		rm -rf "${TEMPMODULEDIR}"
	done
fi

echo "Build successful !"
exit 0
